package com.xixibaby.validator;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 重载spring RequestResponseBodyMethodProcessor
 * 实现参数校检
 * @author tooomj
 */
@Slf4j
public class XixibabyRequestResponseBodyMethodProcessor extends RequestResponseBodyMethodProcessor {


    public XixibabyRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
        super(converters);
    }

    @Getter
    @Setter
    private static class FieldValidType{
        private Field fieldName;
        private ApiValid valid;
        private String standard;
        public FieldValidType(Field fieldName,ApiValid valid){
            this.fieldName = fieldName;
            this.valid = valid;
        }
    }

    private List<FieldValidType> fields(Class<?> clazz,String path){
        Field[] declaredFields = clazz.getDeclaredFields();
        List<FieldValidType> result = new ArrayList<>();
        for (int i = 0; i < declaredFields.length; i++) {
            Field field = declaredFields[i];
            ApiValid apiValid = field.getAnnotation(ApiValid.class);
            if(null != apiValid){
                String join = String.join(",", Arrays.asList(apiValid.actions()));
                if(join.contains(path)){
                    result.add(new FieldValidType(field, apiValid));
                }
            }else{
                ApiValid.List annotation = field.getAnnotation(ApiValid.List.class);
                if (null != annotation && annotation.value().length != 0) {
                    List<ApiValid> collect = Arrays.stream(annotation.value()).collect(Collectors.toList());
                    collect.forEach(x -> {
                                Stream.of(x.actions()).forEach(y ->{
                                    if(y.contains(path)){
                                        result.add(new FieldValidType(field, x));
                                    }
                                });
                            });
                }
            }

        }
        return result;
    }

    private void validataIfApplicable(WebDataBinder binder, MethodParameter parameter){
        Method method = parameter.getMethod();
        PostMapping annotation = method.getAnnotation(PostMapping.class);
        if(null == annotation){
            return;
        }

        Object target = binder.getBindingResult().getTarget();
        List<FieldValidType> fields = fields(parameter.getParameterType(), annotation.value()[0]);
        for (FieldValidType field : fields) {
            Object fieldValue = ReflectUtil.getFieldValue(target, field.getFieldName());
            ApiValid valid = field.getValid();
            ValidEnum validEnum = valid.type();
            boolean test = validEnum.getValid().test(valid.value(), fieldValue);
            if(!test){
                String format = StrUtil.format(validEnum.getMessage(), field.getFieldName().getName(), valid.value());
                log.info("接口[{}]校检失败，失败原因: {}",annotation.value()[0],format);
                ObjectError error = new ObjectError(field.getFieldName().getName(), format);
                binder.getBindingResult().addError(error);
                break;
            }
        }
    }

    @Override
    protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
        //先走自定义的线路
        this.validataIfApplicable(binder,parameter);
        super.validateIfApplicable(binder,parameter);

    }


}
